package seatiger.tomcat.catalina.core;

import seatiger.tomcat.catalina.*;
import seatiger.tomcat.catalina.deploy.FilterDef;
import seatiger.tomcat.catalina.deploy.FilterMap;
import seatiger.tomcat.catalina.loader.WebappLoader;
import seatiger.tomcat.catalina.session.FileStore;
import seatiger.tomcat.catalina.session.PersistentManager;
import seatiger.tomcat.catalina.session.StandardManager;
import seatiger.tomcat.naming.resources.BaseDirContext;
import seatiger.tomcat.naming.resources.FileDirContext;
import seatiger.tomcat.util.http.mapper.MapperRegistry;

import javax.naming.directory.DirContext;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * context 容器
 */
public class StandardContext extends ContainerBase implements Context {

    /**
     * Context 容器设置的项目目录
     */
    private String docBase;
    private Map<String,String> servletMappings = new ConcurrentHashMap<>();

    /**
     * 过滤器的定义
     */
    private Map<String,FilterDef> filterDefs = new ConcurrentHashMap<>();

    /**
     * filter的配置
     */
    private Map<String, FilterConfig> filterConfigs = new HashMap();

    private List<FilterMap> filterMaps = new ArrayList<>();

    public StandardContext(){
        pipeline.setBasic(new StandardContextValve());
    }

    /**
     * 和这个Context关联的ServletContext
     */
    private ApplicationContext context = null;

    private String mapperClass =
            "seatiger.tomcat.catalina.core.StandardContextMapper";

    /*private seatiger.tomcat.util.http.mapper.Mapper mapper =
            new seatiger.tomcat.util.http.mapper.Mapper();*/

    /**
     * context的子容器只能是wrapper
     * @param child
     */
    @Override
    public void addChild(Container child) {
        if (!(child instanceof Wrapper)){
            throw new IllegalArgumentException("standardContext.notWrapper");
        }
        super.addChild(child);
    }

    /**
     * uri 到 servlet name的映射
     * @param pattern
     * @param name
     */
    @Override
    public void addServletMapping(String pattern, String name) {
        if (findChild(name) == null){
            throw new IllegalArgumentException("standardContext.servletMap.name");
        }
        servletMappings.put(pattern,name);
        MapperRegistry.registryWrapper(getParent().getName(),getPath(),pattern,findChild(name));
    }

    /**
     * 测试的 invoke 方法
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     */
    @Override
    @Deprecated
    public void invoke(Request request, Response response) throws IOException, ServletException {
        pipeline.invoke(request,response);
    }

    @Override
    public String findServletMapping(String pattern) {
        return servletMappings.get(pattern);
    }

    @Override
    public String[] findServletMappings() {
        Collection<String> values = servletMappings.keySet();
        return values.toArray(new String[values.size()]);
    }

    @Override
    public void start() throws LifecycleException {
        Logger logger = getLogger();
        logger.log("StandardContext starting.....");
        DirContext resources = getResources();
        if ( resources == null){
            setResources(new FileDirContext());
            ((BaseDirContext) getResources()).setDocBase(getBasePath());
        }
        if (getLoader() == null){
            setLoader(new WebappLoader(this.getClass().getClassLoader()));
        }
        //设置默认的session管理器
        if (getManager() == null){
            setManager(new StandardManager());
        }
        super.start();
        filterStart();
    }

    /**
     *配置和初始化Filter
     */
    private void filterStart() {
        filterConfigs.clear();
        filterDefs.keySet().forEach(filterName -> {
            try {
                ApplicationFilterConfig config =
                        new ApplicationFilterConfig(this,filterDefs.get(filterName));
                filterConfigs.put(filterName,config);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        });
    }

    @Override
    public void setDocBase(String docBase) {
        this.docBase = docBase;
    }

    @Override
    public String getDocBase() {
        return docBase;
    }

    /**
     * 获取基础路径
     * @return
     */
    private String getBasePath(){
        String docBase;
        Container container = this;
        //找到主机Host
        while (container != null){
            if (container instanceof Host){
                break;
            }
            container = this.getParent();
        }
        //没有对应的主机
        if (container == null){
            docBase = new File(engineBase(),getDocBase()).getPath();
        }else{
            //如果有主机，先判断Context中路径是不是绝对路径
            File file = new File(getDocBase());
            //不是绝对路径，就需要获取到绝对路径
            if (!file.isAbsolute()){
                //获取host配置的路径
                String appBase = ((Host) container).getAppBase();
                //判断host配置的是不是绝对路径
                file = new File(appBase);
                if (!file.isAbsolute()){
                    file = new File(engineBase(), appBase);
                }
                docBase = new File(file,getDocBase()).getPath();
            }else {
                docBase = file.getPath();
            }
        }
        return docBase;
    }

    /**
     * 获取 catalina.base 设置中的路径
     * @return
     */
    protected File engineBase() {
        return new File(System.getProperty("catalina.base"));
    }

    @Override
    public void addFilterDef(FilterDef filterDef) {
        filterDefs.put(filterDef.getFilterName(),filterDef);
    }


    @Override
    public List<FilterDef> findFilterDefs() {
        return filterDefs.values().stream().collect(Collectors.toList());
    }

    @Override
    public FilterConfig findFilterConfig(String filterName) {
        return filterConfigs.get(filterName);
    }

    @Override
    public void addFilterMap(FilterMap filterMap) {
        filterMaps.add(filterMap);
    }

    @Override
    public List<FilterMap> findFilterMaps() {
        return filterMaps;
    }

    @Override
    public ServletContext getServletContext() {
        if (context == null){
            context = new ApplicationContext(getBasePath(),this);
        }
        return context;
    }

    @Override
    public void reload() {
        if (!started){
            throw new IllegalStateException(sm.getString("containerBase.notStarted",logName()));
        }
        log(sm.getString("standardContext.reloadingStarted"));
        try {
            stopComponents(manager);
        } catch (LifecycleException e) {
            log(sm.getString("standardContext.stoppingManager"), e);
        }
        Container[] children = findChildren();
        Arrays.stream(children).forEach(wrapper -> {
            try {
                stopComponents(wrapper);
            } catch (LifecycleException e) {
                log(sm.getString("standardContext.stoppingWrapper",wrapper.getName()),e);
            }
        });

        try {
            stopComponents(loader);
        } catch (LifecycleException e) {
            log(sm.getString("standardContext.stoppingLoader"), e);
        }
        try {
            startComponents(loader);
        } catch (LifecycleException e) {
            log(sm.getString("standardContext.startingLoader"), e);
        }
        //启动监听器
        filterStart();
        Arrays.stream(children).forEach(wrapper -> {
            try {
                startComponents(wrapper);
            } catch (LifecycleException e) {
                log(sm.getString("standardContext.startingWrapper",wrapper.getName()),e);
            }
        });

        try {
            startComponents(manager);
        } catch (LifecycleException e) {
            log(sm.getString("standardContext.startingManager"), e);
        }
        lifecycle.fireLifecycleEvent(Context.RELOAD_EVENT, null);
    }

    @Override
    public void setPath(String path) {
        setName(path);
    }

    @Override
    public String getPath() {
        return getName();
    }

    @Override
    protected void addDefaultMapper(String mapperClass) {
        super.addDefaultMapper(this.mapperClass);
    }

    @Override
    public void removeFilterDef(FilterDef filterDef) {
        filterDefs.remove(filterDef.getFilterName());
    }

    @Override
    public void removeFilterMap(FilterMap filterMap) {
        filterMaps.remove(filterMap);
    }

    @Override
    public void stop() throws LifecycleException {
        if (!started){
            throw new LifecycleException
                    (sm.getString("containerBase.notStarted", logName()));
        }
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
        //停止filter
        filterStop();
        stopComponents(manager);
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        stopComponents(pipeline);
        //这里应该没有孩子节点了，已经在HostConfig中全部移除了
        stopComponents(findChildren());
        stopComponents(findMappers());
        stopComponents(resources);
        resources = null;
        stopComponents(logger);
        stopComponents(loader);
        context = null;
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
    }

    private void filterStop() {
        filterConfigs.forEach((key,value) -> {
            ((ApplicationFilterConfig) value).release();
        });
        filterConfigs.clear();
    }

    @Override
    public void removeServletMapping(String pattern) {
        servletMappings.remove(pattern);
    }

    @Override
    public void init() {
        MapperRegistry.registryContext(getParent().getName(),getPath(),this);
        initialized = true;
    }
}
